package net.ufrog.common.data;

import net.ufrog.common.data.spring.Domains;
import net.ufrog.common.utils.Objects;
import net.ufrog.common.utils.Strings;
import org.springframework.data.domain.Sort;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-01-27
 * @since 0.1
 */
public class QueryScript implements Serializable {

    private static final long serialVersionUID = -2881902987039010787L;

    private static final String KEYWORD_FROM    = " from ";
    private static final String KEYWORD_ORDER   = " order by ";

    /** 脚本 */
    private StringBuffer script;

    /** 参数 */
    private Map<String, Object> args;

    /** 顺序 */
    private String order;

    /**
     * 构造函数
     *
     * @param script 脚本
     */
    public QueryScript(String script) {
        this.script = new StringBuffer();
        this.args = new HashMap<>();
        this.parse(script);
    }

    /**
     * 追加脚本内容
     *
     * @param part 追加部分
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript append(String part, Map<String, Object> args) {
        this.script.append(part);
        this.args.putAll(args);
        return this;
    }

    /**
     * 追加脚本内容
     *
     * @param part 追加部分
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript append(String part, Object... args) {
        return append(part, Objects.map(args));
    }

    /**
     * 且条件
     *
     * @param part 追加部分
     * @param condition 条件
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript and(String part, Boolean condition, Map<String, Object> args) {
        if (condition) {
            this.script.append(" and ").append(part);
            this.args.putAll(args);
        }
        return this;
    }

    /**
     * 且条件
     *
     * @param part 追加部分
     * @param condition 条件
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript and(String part, Boolean condition, Object... args) {
        return and(part, condition, Objects.map(args));
    }

    /**
     * 或条件
     *
     * @param part 追加部分
     * @param condition 条件
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript or(String part, Boolean condition, Map<String, Object> args) {
        if (condition) {
            this.script.append(" or ").append(part);
            this.args.putAll(args);
        }
        return this;
    }

    /**
     * 或条件
     *
     * @param part 追加部分
     * @param condition 条件
     * @param args 参数
     * @return 查询脚本本身
     */
    public QueryScript or(String part, Boolean condition, Object... args) {
        return or(part, condition, Objects.map(args));
    }

    /**
     * 排序
     *
     * @param order 排序脚本
     * @return 查询脚本本身
     */
    public QueryScript order(String order) {
        this.order = order;
        return this;
    }

    /**
     * 排序
     *
     * @param sort 排序
     * @return 查询脚本本身
     */
    public QueryScript order(Sort sort) {
        if (sort != null) {
            this.order = Domains.toOrderString(sort);
        }
        return this;
    }

    /**
     * 排序
     *
     * @param orders 排序
     * @return 查询脚本本身
     */
    public QueryScript order(Sort.Order... orders) {
        return order(new Sort(orders));
    }

    /**
     * 获取查询脚本
     *
     * @return 脚本字符串
     */
    public String getScript() {
        return script.toString() + (hasOrder() ? KEYWORD_ORDER + order : "");
    }

    /**
     * 获取不含排序子句的查询脚本
     *
     * @return 脚本字符串
     */
    public String getScriptWithoutOrder() {
        return script.toString();
    }

    /**
     * 获取统计脚本
     *
     * @return 脚本字符串
     */
    public String getCountScript() {
        Integer fromIndex = (" " + script).toLowerCase().indexOf(KEYWORD_FROM);
        return "select count(*) " + (fromIndex == 0 ? script : script.substring(fromIndex));
    }

    /**
     * 读取参数
     *
     * @return 参数映射
     */
    public Map<String, Object> getArguments() {
        return args;
    }

    /**
     * 判断是否存在排序子句
     *
     * @return 判断结果
     */
    public Boolean hasOrder() {
        return !Strings.empty(order);
    }

    /**
     * 解析脚本
     *
     * @param script 脚本
     */
    private void parse(String script) {
        Integer orderIndex = script.indexOf(KEYWORD_ORDER);
        this.append(orderIndex > 0 ? script.substring(0, orderIndex) : script);
        this.order(orderIndex > 0 ? script.substring(orderIndex + KEYWORD_ORDER.length()) : null);
    }
}
